
#include <windows.h>
#include "tf_OpenglDC.h"
#include <vector>
#include "../TF_Geom/tf_Line.h"
#include "../TF_Geom/tf_Element.h"
using namespace std;
#define GL_PI 3.1415f



//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


TfOpenGLDC::TfOpenGLDC(HWND hWnd):m_hWnd(hWnd)
{
	m_bSelectionMode = FALSE;
}

TfOpenGLDC::~TfOpenGLDC()
{
}

BOOL TfOpenGLDC::InitDC()
{
	if (m_hWnd == NULL) return FALSE;
	
	m_Camera.Init();

	m_hDC = ::GetDC(m_hWnd);			// Get the Device context

	int pixelformat;
	PIXELFORMATDESCRIPTOR pfdWnd =
	{
		sizeof(PIXELFORMATDESCRIPTOR), // Structure size.
		1,                             // Structure version number.
		PFD_DRAW_TO_WINDOW |           // Property flags.
		PFD_SUPPORT_OPENGL |
 		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		24,                            // 24-bit color.
		0, 0, 0, 0, 0, 0,              // Not concerned with these.
		0, 0, 0, 0, 0, 0, 0,           // No alpha or accum buffer.
		32,                            // 32-bit depth buffer.
		0, 0,                          // No stencil or aux buffer.
		PFD_MAIN_PLANE,                // Main layer type.
		0,                             // Reserved.
		0, 0, 0                        // Unsupported.
	};

    if ( (pixelformat = ChoosePixelFormat(m_hDC, &pfdWnd)) == 0 )
	{
// 		AfxMessageBox("ChoosePixelFormat to wnd failed");
		return FALSE;
	}

     if (SetPixelFormat(m_hDC, pixelformat, &pfdWnd) == FALSE)
	 {
		 return FALSE;
	 }
//         AfxMessageBox("SetPixelFormat failed");

	m_hRC=wglCreateContext(m_hDC);

//	VERIFY(wglMakeCurrent(m_hDC,m_hRC));

	wglMakeCurrent(m_hDC,m_hRC);
	GLSetupRC();
	wglMakeCurrent(NULL,NULL);
	return m_hRC!=0;
}


void TfOpenGLDC::GLResize(int w,int h)
{
	wglMakeCurrent(m_hDC,m_hRC);

	// Prevent a divide by zero
	if(h == 0) h = 1;
	if(w == 0) w = 1;
	m_Camera.SetScreen(w,h);


}

void TfOpenGLDC::GLSetupRC()
{
	//initialize color and rendering
	m_bShading = TRUE;

	//bright white light - full intensity RGB values
	GLfloat lightAmbient[] = {0.75f,0.75f,0.75f,1.0f};
	GLfloat lightDiffuse[] = {1.0f,1.0f,1.0f,1.0f};

	glEnable(GL_DEPTH_TEST);		//Hidden surface removal
	glEnable(GL_CULL_FACE);			//Do not calculate inside of object
	glFrontFace(GL_CCW);			//counter clock-wise polygons face out

	glEnable(GL_LIGHTING);			//enable lighting

	//setup and enable light 0
	glLightfv(GL_LIGHT0,GL_AMBIENT,lightAmbient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,lightDiffuse);
	SetLightDirection(1,1,1);
	glEnable(GL_LIGHT0);

	//Initialize Material Color to Gray
	SetBkColor(RGB(0,0,0));				//black background
	SetMaterialColor(RGB(225,175,22));	//golden material color
	SetColor(RGB(255,255,255));			//white frame color
	SetHighlightColor(RGB(255,0,0));	//red highlight color	

	//Point Size
	glPointSize(3.0);
}


void TfOpenGLDC::Ready()
{
	wglMakeCurrent(m_hDC,m_hRC);


	ClearBkground();
	OnShading();
	m_Camera.Projection();
}

void TfOpenGLDC::Finish()
{
	glFlush();
	SwapBuffers(m_hDC);
	wglMakeCurrent(m_hDC,NULL);
}


//////////////////LIGHT && MATERIALS SETTING//////////
void TfOpenGLDC::ClearBkground()
{
	GLclampf	r,g,b;
	r = (GLclampf)GetRValue(m_clrBk)/255.0;
	g = (GLclampf)GetGValue(m_clrBk)/255.0;
	b = (GLclampf)GetBValue(m_clrBk)/255.0;

	glClearColor(r,g,b,0.0f);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}

//setting model
void TfOpenGLDC::OnShading()
{
	if(m_bShading){
		glEnable( GL_LIGHTING );
	    glEnable(GL_LIGHT0);
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
	}
	else{
		glDisable( GL_LIGHTING );
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
	}
}

void TfOpenGLDC::Shading(BOOL bShading)
{
	m_bShading = bShading;
}

BOOL TfOpenGLDC::IsShading()
{
	return m_bShading;
}

void TfOpenGLDC::Lighting(BOOL bLighting)
{
	if(bLighting)
		glEnable( GL_LIGHTING );
	else
		glDisable( GL_LIGHTING );
}

BOOL TfOpenGLDC::IsLighting()
{
	GLboolean bLighting;
	glGetBooleanv(GL_LIGHTING,&bLighting);   
	return bLighting;
}

void TfOpenGLDC::SetLightDirection(float dx,float dy,float dz)
{
	m_vecLight[0] = dx;
	m_vecLight[1] = dy;
	m_vecLight[2] = dz;
	GLfloat lightPos[] = { dx, dy, dz, 0.0f };
	glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
}

void TfOpenGLDC::GetLightDirection(float& dx,float& dy,float& dz)
{
	dx = m_vecLight[0];
	dy = m_vecLight[1];
	dz = m_vecLight[2];
}

//rendering color
void TfOpenGLDC::SetMaterialColor(COLORREF clr)
{
	m_clrMaterial = clr;
	BYTE r,g,b;
	r = GetRValue(clr);
	g = GetGValue(clr);
	b = GetBValue(clr);
	GLfloat mat_amb_diff[] = {(GLfloat)r/255,(GLfloat)g/255,(GLfloat)b/255,1.0};
	glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_amb_diff);
}

void TfOpenGLDC::GetMaterialColor(COLORREF& clr)
{
	clr = m_clrMaterial;
}

void TfOpenGLDC::SetBkColor(COLORREF clr)
{
	m_clrBk = clr;
}

void TfOpenGLDC::GetBkColor(COLORREF& clr)
{
	clr = m_clrBk;
}

void TfOpenGLDC::SetColor(COLORREF clr)
{
	m_clr = clr;
	BYTE r,g,b;
	r = GetRValue(clr);
	g = GetGValue(clr);
	b = GetBValue(clr);
	glColor3ub(r,g,b);
}

void TfOpenGLDC::GetColor(COLORREF& clr)
{
	clr =  m_clr;
}

void TfOpenGLDC::SetHighlightColor(COLORREF clr)
{
	m_clrHighlight = clr;
}

void TfOpenGLDC::GetHighlightColor(COLORREF& clr)
{
	clr = m_clrHighlight;
}

void TfOpenGLDC::Highlight(BOOL bHighlight)
{
	BYTE r,g,b;
	if(bHighlight){
		r = GetRValue(m_clrHighlight);
		g = GetGValue(m_clrHighlight);
		b = GetBValue(m_clrHighlight);
	}
	else{
		r = GetRValue(m_clrMaterial);
		g = GetGValue(m_clrMaterial);
		b = GetBValue(m_clrMaterial);
	}
	GLfloat mat_amb_diff[] = {(GLfloat)r/255,(GLfloat)g/255,(GLfloat)b/255,1.0};
	glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_amb_diff);
}


//////////////////////// drawing functions ////////////////////////
//draw point
// void TfOpenGLDC::DrawPoint(const CPoint3D& pt)
// {
// 	glBegin(GL_POINTS);
// 		glVertex3f(pt.x,pt.y,pt.z);
// 	glEnd();
// }
// 
// void TfOpenGLDC::DrawLine(const CPoint3D& sp,const CPoint3D& ep)
// {
// 	glBegin(GL_LINES);
// 		glVertex3f(sp.x,sp.y,sp.z);
// 		glVertex3f(ep.x,ep.y,ep.z);
// 	glEnd();
// }

// void TfOpenGLDC::DrawPolyline(const CPoint3D* pt,int size)
// {
// 	glBegin(GL_LINE_STRIP);
// 	for(int i=0;i<size;i++)
// 		glVertex3f(pt[i].x,pt[i].y,pt[i].z);
// 	glEnd();
// }

void TfOpenGLDC::DrawTriChip(double n0,double n1,double n2,double v00,double v01,double v02,
					double v10,double v11,double v12,double v20,double v21,double v22)
{
	glBegin(GL_TRIANGLES);
		glNormal3d(n0,n1,n2);
		glVertex3d(v00,v01,v02);
		glVertex3d(v10,v11,v12);
		glVertex3d(v20,v21,v22);
	glEnd();
}


void TfOpenGLDC::DrawCoord()
{
	BOOL bLighting = IsLighting();
	Lighting(FALSE);
	
	double width,height;
	m_Camera.GetViewRect(width,height);
	double len = min(width,height);
	len *= 0.2;
	
	
	
	glBegin(GL_LINES);

	COLORREF old_clr;
	GetColor(old_clr);
	
	//axis-x: red
	SetColor(RGB(255,0,0));
	glVertex3d(0.0,0.0,0.0);
	glVertex3d(1000.0,0.0,0.0);
	
	//axis-y: green
	SetColor(RGB(0,255,0));
	glVertex3d(0.0,0.0,0.0);
	glVertex3d(0.0,1000.0,0.0);
	
	//axis-z: blue
	SetColor(RGB(0,0,255));
	glVertex3d(0.0,0.0,0.0);
	glVertex3d(0.0,0.0,1000.0);
	
	Lighting(bLighting);
	SetColor(old_clr);

	glEnd();

}

void TfOpenGLDC::DrawLine()
{

	BOOL bLighting = IsLighting();
	Lighting(FALSE);
	
	double width,height;
	m_Camera.GetViewRect(width,height);
	double len = min(width,height);
	len *= 0.2;


	int i,j;
	vector<int> partLineVec;
	vector<int> ndLineVec;
	double pt1[3],pt2[3];
	glBegin(GL_LINES);
	TfLinMng.GetPartLines(1,partLineVec);
	for (i = 0; i < partLineVec.size();i++)
	{
		TfLinMng.GetLineNodes(partLineVec[i],ndLineVec);
		for (j = 0; j < ndLineVec.size()-1;j++)
		{
			TfNdMng.GetNodePoint(ndLineVec[j],pt1);
			TfNdMng.GetNodePoint(ndLineVec[j+1],pt2);
			glVertex3d(pt1[0],pt1[1],pt1[2]);
			glVertex3d(pt2[0],pt2[1],pt2[2]);
		}	
		ndLineVec.clear();
	}
	glEnd();
}
void TfOpenGLDC::DrawElement()
{


	BOOL bLighting = IsLighting();
	Lighting(FALSE);
	
	double width,height;
	m_Camera.GetViewRect(width,height);
	double len = min(width,height);
	len *= 0.2;


	int i,j;
	vector<int> partEleVec;
	vector<int> ndEleVec;
	double pt[3];
	glBegin(GL_TRIANGLES);
	TfEmMng.GetPartElements(1,partEleVec);
	for (i = 0; i < partEleVec.size();i++)
	{
		TfEmMng.GetElementNodes(partEleVec[i],ndEleVec);
		for (j = 0; j < ndEleVec.size();j++)
		{
			TfNdMng.GetNodePoint(ndEleVec[j],pt);
			glVertex3f(pt[0],pt[1],pt[2]);
		}
		ndEleVec.clear();
	}
	glEnd();
}

//////////////////////////////////////////////////
void TfOpenGLDC::BeginSelection(int xPos,int yPos)
{
	m_bSelectionMode = TRUE;
	wglMakeCurrent(m_hDC,m_hRC);
		
	//set up selection buffer	
	glSelectBuffer(BUFFER_LENGTH,m_selectBuff);
	
	//switch to projection and save the matrix
	m_Camera.Selection(xPos,yPos);

	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	InitNames();
}

int	TfOpenGLDC::EndSelection(UINT* items)
{
	m_bSelectionMode = FALSE;
	int hits = glRenderMode(GL_RENDER);
	for(int i=0;i<hits;i++){
		items[i] = m_selectBuff[i*4+3];
	}
	wglMakeCurrent(m_hDC,NULL);
	return hits;
}

BOOL TfOpenGLDC::IsSelectionMode()
{
	return m_bSelectionMode;
}

void TfOpenGLDC::InitNames()
{
	glInitNames();
	glPushName(0);
}

void TfOpenGLDC::LoadName(UINT name)
{
	glLoadName(name);
}

void TfOpenGLDC::PushName(UINT name)
{
	glPushName(name);
}

void TfOpenGLDC::PopName()
{
	glPopName();
}